/*
 * RelativeLayout.cpp
 *
 *  Created on: 2012-10-25
 *      Author: yangentao@gmail.com
 */

#include <stack>
#include "RelativeLayout.h"
#include "Window.h"

using namespace std;

RelativeParam* getParam(Window* w) {
	return w->getRelativeParam();
}

void RelativeLayout::layout(Window* parent) {
	Rect clientRect = parent->getClientRect();
	Vector<Window*> children = parent->getChildren(true, relativeLayoutParam);
	for (int i = 0; i < children.size(); ++i) {
		RelativeParam* p = getParam(children[i]);
		p->clear();
	}

	bool success = checkCircle(children, parent);
	if (!success) {
		Log << error << "Relative Has Circle!!" << endl;
		return;
	}
	fixChildren(children, parent);

	doMove(children, parent);
}
void RelativeLayout::fixChildren(const Vector<Window*>& children, Window* parent) {
	for (int i = 0; i < children.size(); ++i) {
		fixOne(children.get(i), parent);
	}
}
bool RelativeLayout::checkCircle(const Vector<Window*>& children, Window* parent) {
	for (int i = 0; i < children.size(); ++i) {
		Window* w = children.get(i);
		Vector<Window*> vecH(8);
		Vector<Window*> vecV(8);
		getDepends(w, vecH, vecV);
		if (vecH.contains(w) || vecV.contains(w)) {
			return false;
		}
	}
	return true;
}

void RelativeLayout::doMove(const Vector<Window*>& children, Window* parent) {
	for (int i = 0; i < children.size(); ++i) {
		Window* child = children.get(i);
		RelativeParam* p = getParam(child);
		Rect& r = p->rect;
		if (p->leftEnable && p->rightEnable && p->topEnable && p->bottomEnable) {
			child->moveWindow(r.left + p->marginLeft, r.top + p->marginTop, r.Width() - p->marginLeft - p->marginRight, r.Height() - p->marginTop - p->marginBottom, TRUE);
		}
	}
}
void RelativeLayout::getDepends(Window* w, Vector<Window*>& vecH, Vector<Window*>& vecV) {
	if (!w) {
		return;
	}
	RelativeParam* param = getParam(w);
	{
		Window* w1 = param->alignAnchorLeft;
		Window* w2 = param->alignAnchorRight;
		Window* w3 = param->toAnchorLeft;
		Window* w4 = param->toAnchorRight;
		if (w1 && !vecH.contains(w1)) {
			vecH.append(w1);
			getDepends(w1, vecH, vecV);
		}
		if (w2 && !vecH.contains(w2)) {
			vecH.append(w2);
			getDepends(w2, vecH, vecV);
		}
		if (w3 && !vecH.contains(w3)) {
			vecH.append(w3);
			getDepends(w3, vecH, vecV);
		}
		if (w4 && !vecH.contains(w4)) {
			vecH.append(w4);
			getDepends(w4, vecH, vecV);
		}
	}
	{
		Window* w1 = param->alignAnchorTop;
		Window* w2 = param->alignAnchorBottom;
		Window* w3 = param->toAnchorTop;
		Window* w4 = param->toAnchorBottom;
		if (w1 && !vecV.contains(w1)) {
			vecV.append(w1);
			getDepends(w1, vecH, vecV);
		}
		if (w2 && !vecV.contains(w2)) {
			vecV.append(w2);
			getDepends(w2, vecH, vecV);
		}
		if (w3 && !vecV.contains(w3)) {
			vecV.append(w3);
			getDepends(w3, vecH, vecV);
		}
		if (w4 && !vecV.contains(w4)) {
			vecV.append(w4);
			getDepends(w4, vecH, vecV);
		}
	}
}
void RelativeLayout::fixOne(Window* child, Window* parent) {
	if (!child) {
		return;
	}
	Rect clientRect = parent->getClientRect();
	RelativeParam* p = getParam(child);
	if (p->leftEnable && p->rightEnable && p->topEnable && p->bottomEnable) {
		return;
	}

	assert(child != p->alignAnchorLeft);
	assert(child != p->alignAnchorRight);
	assert(child != p->alignAnchorTop);
	assert(child != p->alignAnchorBottom);
	assert(child != p->toAnchorLeft);
	assert(child != p->toAnchorRight);
	assert(child != p->toAnchorTop);
	assert(child != p->toAnchorBottom);

	fixOne(p->alignAnchorLeft, parent);
	fixOne(p->alignAnchorRight, parent);
	fixOne(p->alignAnchorTop, parent);
	fixOne(p->alignAnchorBottom, parent);
	fixOne(p->toAnchorLeft, parent);
	fixOne(p->toAnchorRight, parent);
	fixOne(p->toAnchorTop, parent);
	fixOne(p->toAnchorBottom, parent);

	Rect& r = p->rect;
	RelativeSizeValue& width = p->width;
	RelativeSizeValue& height = p->height;

	if ((p->centerHor || p->centerInParent)) {
		if (width.isFix()) {
			r.left = (clientRect.Width() - width.fixValue()) / 2;
			r.right = r.left + width.fixValue();
		} else {
			r.left = clientRect.left;
			r.right = clientRect.right;
		}
		p->leftEnable = true;
		p->rightEnable = true;
	}
	if ((p->centerVer || p->centerInParent)) {
		if (height.isFix()) {
			r.top = (clientRect.Height() - height.fixValue()) / 2;
			r.bottom = r.top + height.fixValue();
		} else {
			r.top = clientRect.top;
			r.bottom = clientRect.bottom;
		}
		p->topEnable = true;
		p->bottomEnable = true;
	}

	if (!p->leftEnable) {
		if (p->alignParentLeft) {
			r.left = clientRect.left;
			p->leftEnable = true;
		}

		if (!p->leftEnable && p->alignAnchorLeft) {
			RelativeParam* ap = getParam(p->alignAnchorLeft);
			assert(ap->leftEnable);
			r.left = ap->rect.left;
			p->leftEnable = true;
		}
		if (!p->leftEnable && p->toAnchorRight) {
			RelativeParam* ap = getParam(p->toAnchorRight);
			assert(ap->rightEnable);
			r.left = ap->rect.right;
			p->leftEnable = true;

		}
	}
	if (!p->rightEnable) {
		if (p->alignParentRight) {
			r.right = clientRect.right;
			p->rightEnable = true;
		}
		if (!p->rightEnable && p->alignAnchorRight) {
			RelativeParam* ap = getParam(p->alignAnchorRight);
			assert(ap->rightEnable);
			r.right = ap->rect.right;
			p->rightEnable = true;

		}
		if (!p->rightEnable && p->toAnchorLeft) {
			RelativeParam* ap = getParam(p->toAnchorLeft);
			assert(ap->leftEnable);
			r.right = ap->rect.left;
			p->rightEnable = true;

		}
	}

	if (!p->topEnable) {
		if (p->alignParentTop) {
			r.top = clientRect.top;
			p->topEnable = true;
		}

		if (!p->topEnable && p->alignAnchorTop) {
			RelativeParam* ap = getParam(p->alignAnchorTop);
			assert(ap->topEnable);
			r.top = ap->rect.top;
			p->topEnable = true;

		}
		if (!p->topEnable && p->toAnchorBottom) {
			RelativeParam* ap = getParam(p->toAnchorBottom);
			assert(ap->bottomEnable);
			r.top = ap->rect.bottom;
			p->topEnable = true;

		}

	}
	if (!p->bottomEnable) {
		if (p->alignParentBottom) {
			r.bottom = clientRect.bottom;
			p->bottomEnable = true;
		}

		if (!p->bottomEnable && p->alignAnchorBottom) {
			RelativeParam* ap = getParam(p->alignAnchorBottom);
			assert(ap->bottomEnable);
			r.bottom = ap->rect.bottom;
			p->bottomEnable = true;

		}
		if (!p->bottomEnable && p->toAnchorTop) {
			RelativeParam* ap = getParam(p->toAnchorTop);
			assert(ap->topEnable);
			r.bottom = ap->rect.top;
			p->bottomEnable = true;

		}
	}
	calcFix(p, clientRect);
}
void RelativeLayout::calcFix(RelativeParam* p, Rect clientRect) {
	Rect& r = p->rect;
	RelativeSizeValue& width = p->width;
	RelativeSizeValue& height = p->height;

	int fillW = 0;
	if (width.isFill()) {
		fillW = width.calcSize(clientRect.Width());

	}
	int fillH = 0;
	if (height.isFill()) {
		fillH = height.calcSize(clientRect.Height());
	}
	if (p->leftEnable) {
		if (!p->rightEnable) {
			if (width.isFix()) {
				r.right = r.left + width.fixValue();
			} else {
				r.right = Min(r.left + fillW, clientRect.right);
			}
			p->rightEnable = true;
		} else {
			//do nothing
		}
	} else {
		if (p->rightEnable) {
			if (width.isFix()) {
				r.left = r.right - width.fixValue();
			} else {
				r.left = Max(r.right - fillW, clientRect.left);
			}
			p->leftEnable = true;
		} else { //!left and !right
			if (width.isFill()) {
				r.left = (clientRect.Width() - fillW) / 2;
				r.right = r.left + fillW;
				p->leftEnable = true;
				p->rightEnable = true;
			}
		}

	}
	if (p->topEnable) {
		if (!p->bottomEnable) {
			if (height.isFix()) {
				r.bottom = r.top + height.fixValue();
			} else {
				r.bottom = Min(r.top + fillH, clientRect.bottom);
			}
			p->bottomEnable = true;
		} else {
			//do nothing
		}
	} else {
		if (p->bottomEnable) {
			if (height.isFix()) {
				r.top = r.bottom - height.fixValue();
			} else {
				r.top = Max(r.bottom - fillH, clientRect.top);
			}
			p->topEnable = true;
		} else {
			if (height.isFill()) {
				r.top = (clientRect.Height() - fillH) / 2;
				r.bottom = r.top + fillH;
				p->topEnable = true;
				p->bottomEnable = true;
			}
		}
	}
}
RelativeLayout::RelativeLayout() {

}

RelativeLayout::~RelativeLayout() {
}

